/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.unesp.ia.gridsolver.csp;

import com.unesp.ia.gridsolver.entity.Aluno;
import com.unesp.ia.gridsolver.entity.Curso;
import com.unesp.ia.gridsolver.entity.Materia;
import com.unesp.ia.gridsolver.entity.Slotmateria;
import com.unesp.ia.gridsolver.session.MateriaFacade;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

/**
 *
 * @author Pardal
 */
public class TSPData {   

    public static enum Horario {

        S1_SegManha(0, 0, 0, "S1_Segunda_Manhã"),
        S1_SegTarde(0, 0, 1, "S1_Segunda_Tarde"),
        S1_SegNoite(0, 0, 2, "S1_Segunda_Noite"),
        S1_TerManha(0, 1, 0, "S1_Terça_Manhã"),
        S1_TerTarde(0, 1, 1, "S1_Terça_Tarde"),
        S1_TerNoite(0, 1, 2, "S1_Terça_Noite"),
        S1_QuaManha(0, 2, 0, "S1_Quarta_Manhã"),
        S1_QuaTarde(0, 2, 1, "S1_Quarta_Tarde"),
        S1_QuaNoite(0, 2, 2, "S1_Quarta_Noite"),
        S1_QuiManha(0, 3, 0, "S1_Quinta_Manhã"),
        S1_QuiTarde(0, 3, 1, "S1_Quinta_Tarde"),
        S1_QuiNoite(0, 3, 2, "S1_Quinta_Noite"),
        S1_SexManha(0, 4, 0, "S1_Sexta_Manhã"),
        S1_SexTarde(0, 4, 1, "S1_Sexta_Tarde"),
        S1_SexNoite(0, 4, 2, "S1_Sexta_Noite"),
        S1_SabManha(0, 5, 0, "S1_Sábado_Manhã"),
        S1_SabTarde(0, 5, 1, "S1_Sábado_Tarde"),
        S1_SabNoite(0, 5, 2, "S1_Sábado_Noite"),
        S2_SegManha(1, 0, 0, "S2_Segunda_Manhã"),
        S2_SegTarde(1, 0, 1, "S2_Segunda_Tarde"),
        S2_SegNoite(1, 0, 2, "S2_Segunda_Noite"),
        S2_TerManha(1, 1, 0, "S2_Terça_Manhã"),
        S2_TerTarde(1, 1, 1, "S2_Terça_Tarde"),
        S2_TerNoite(1, 1, 2, "S2_Terça_Noite"),
        S2_QuaManha(1, 2, 0, "S2_Quarta_Manhã"),
        S2_QuaTarde(1, 2, 1, "S2_Quarta_Tarde"),
        S2_QuaNoite(1, 2, 2, "S2_Quarta_Noite"),
        S2_QuiManha(1, 3, 0, "S2_Quinta_Manhã"),
        S2_QuiTarde(1, 3, 1, "S2_Quinta_Tarde"),
        S2_QuiNoite(1, 3, 2, "S2_Quinta_Noite"),
        S2_SexManha(1, 4, 0, "S2_Sexta_Manhã"),
        S2_SexTarde(1, 4, 1, "S2_Sexta_Tarde"),
        S2_SexNoite(1, 4, 2, "S2_Sexta_Noite"),
        S2_SabManha(1, 5, 0, "S2_Sábado_Manhã"),
        S2_SabTarde(1, 5, 1, "S2_Sábado_Tarde"),
        S2_SabNoite(1, 5, 2, "S2_Sábado_Noite");
        private final int semestre;
        private final int diaSem;
        private final int periodo;
        private final String nome;

        private Horario(int semestre, int diaSem, int periodo, String nome) {
            this.semestre = semestre;
            this.diaSem = diaSem;
            this.periodo = periodo;
            this.nome = nome;
        }

        public int getSemestre() {
            return semestre;
        }

        public int getDiaSem() {
            return diaSem;
        }

        public int getPeriodo() {
            return periodo;
        }

        public String getNome() {
            return nome;
        }
    }
    
    
    
    // CSP Vars
    private int nSemestres;
    private int timeDOmain; //Domínio da enumeração dos slots
    private int offset;
    private int[] overDue; //Semestres além do ideal
    private List<TSPMateria> slotsMaterias;
    
    // TSPData vars
    private boolean startByS2;
    private Aluno aluno;
    private Curso curso;
    private List<Materia> materias;

    private MateriaFacade materiaFacade;
    
    public TSPData(Aluno aluno, Curso curso, MateriaFacade materiaFacade) {
        this.aluno = aluno;
        this.curso = curso;
        this.materiaFacade = materiaFacade;
        nSemestres = (curso.getNMaxSemestres() - aluno.getSemestre());
        startByS2 = ((aluno.getSemestre() % 2) != 0) ? true : false;
        offset = aluno.getSemestre() * 18;
        buildMateriasList();
        createCSPData();
    }

    private void buildMateriasList() {
        materias = curso.getMateriaList();
        for (Materia cursada : aluno.getMateriaList()) {
            Iterator<Materia> it = materias.iterator();
            while (it.hasNext()) {
                Materia materia = it.next();
                if (materia.equals(cursada)) {
                    it.remove();
                } else {
                    if (materia.getMateriaList().contains(cursada)) {
                        materia.getMateriaList().remove(cursada);
                    }
                }
            }

        }
    }

    private void createCSPData() {
        // Numero de slots de tempo
        timeDOmain = (nSemestres / 2) * Horario.values().length;
        // Semestres não default
        overDue = new int[curso.getNMaxSemestres() - curso.getNIdealSemestres()];
        for (int i = curso.getNIdealSemestres() + 1; i < (curso.getNMaxSemestres() + 1); i++) {
            overDue[i - (curso.getNIdealSemestres() + 1)] = i;
        }
        // Listas de periodos por matéria
        slotsMaterias = new ArrayList<TSPMateria>();
        for (Materia m : materias) {
            int firstSlot = slotsMaterias.size();
            for (Slotmateria sl : m.getSlotmateriaList()) {
                TSPMateria newSlot = new TSPMateria();
                newSlot.setIdMateria(m.getIdMateria());
                int slotDefault = (m.getSemestreIdeal() / 3) * 36 + sl.getHorario();
                newSlot.setHorarioDefault(slotDefault - offset);
                int slotsOferta = (aluno.getSemestre() / 3) * 36 + sl.getHorario();
                do {
                    newSlot.addHorarioOferta(slotsOferta);
                    slotsOferta += 36;
                } while (slotsOferta < timeDOmain);
                for (int i = 0; i < m.getSlotmateriaList().size(); i++){
                    newSlot.addSlotMateria(i+firstSlot);
                }                
                slotsMaterias.add(newSlot);
            }
        }
        // Atualiza a lista de prerequisitos na lista de lots        
        for (int i = 0; i < slotsMaterias.size(); i++){
            TSPMateria mData = slotsMaterias.get(i);
            Materia pre = materiaFacade.find(mData.getIdMateria());
            for (int j = 0; j < slotsMaterias.size(); j++){
                TSPMateria depData = slotsMaterias.get(j);
                Materia m = materiaFacade.find(depData.getIdMateria());
                if (m.getMateriaList().contains(pre)){
                    depData.addPreRequisito(i);
                }
            }
        }

    }

    public void prettyOut() {
        System.out.println("Semestres penalizados: " + Arrays.toString(overDue));
        System.out.println("Semestres modelados: " + nSemestres + "( " + (aluno.getSemestre() + 1) + " ~ " + curso.getNMaxSemestres() + ")");
        System.out.println("Offset: "+offset);
        for (int i = 0; i < slotsMaterias.size(); i++) {
            TSPMateria mData = slotsMaterias.get(i);
            System.out.println(i+" -----------------------------");
            System.out.println("Matéria: "+materiaFacade.find(mData.getIdMateria()).getNome()+" ("+mData.getIdMateria()+")");
            System.out.println("Horario default: "+mData.getHorarioDefault());
            System.out.println("Horario oferta: "+Arrays.toString(mData.getHorariosOferta()));
            System.out.println("Slots matéria: "+Arrays.toString(mData.getSlotsMateria()));
            System.out.println("Slots pré: "+Arrays.toString(mData.getPreRequisitos()));
            for (int j : mData.getPreRequisitos()){
                TSPMateria preData = slotsMaterias.get(j);
                System.out.println("\t>"+materiaFacade.find(preData.getIdMateria()).getNome());
            }
            System.out.println("---------------------------------\n");
        }
    }
    
    // Setter and Getters
    public int getOffset() {
        return offset;
    }    
    
    public int getnSemestres() {
        return nSemestres;
    }

    public void setnSemestres(int nSemestres) {
        this.nSemestres = nSemestres;
    }

    public int getTimeDOmain() {
        return timeDOmain;
    }

    public void setTimeDOmain(int timeDOmain) {
        this.timeDOmain = timeDOmain;
    }

    public int[] getOverDue() {
        return overDue;
    }

    public void setOverDue(int[] overDue) {
        this.overDue = overDue;
    }

    public List<TSPMateria> getSlots() {
        return slotsMaterias;
    }

    public void setSlots(List<TSPMateria> slotsMaterias) {
        this.slotsMaterias = slotsMaterias;
    }

    public List<Materia> getMaterias() {
        return materias;
    }

    public void setMaterias(List<Materia> materias) {
        this.materias = materias;
    }
}
